#region Copyright Notice
// 
// 
// Copyright (c) 2009-2010 Anindya Chatterjee
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//     http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// 
// 
// 
#endregion
using System.Diagnostics;
using System.IO;
using System.Xml;
using System.Xml.Serialization;

namespace System.Serialization
{
    /// <summary>
    /// Serializer tool to (de)serialize IXObject object
    /// </summary>
    /// <typeparam name="T">A class who implements <see cref="IXObject"/> interface</typeparam>
    public class XObjectSerializer<T> where T : IXObject, new()
    {
        private readonly Type _type;
        private XmlSerializer _xmlSerializer;

        /// <summary>
        /// Initializes a new instance of the <see cref="XObjectSerializer&lt;T&gt;"/> class.
        /// </summary>
        public XObjectSerializer()
        {
            _type = typeof (T);
            _xmlSerializer = new XmlSerializer(_type);
        }

        /// <summary>Deserializes a xml string to an instance of T object</summary>
        /// <param name="xml">Xml string.</param>
        /// <returns>T object</returns>
        public T Deserialize(string xml)
        {
            TextReader reader = new StringReader(xml);
            return Deserialize(reader);
        }

        /// <summary>Deserializes to an instance of Feed.</summary>
        /// <param name="doc">XmlDocument instance.</param>
        /// <returns>Feed result.</returns>
        public T Deserialize(XmlDocument doc)
        {
            TextReader reader = new StringReader(doc.OuterXml);
            return Deserialize(reader);
        }

        /// <summary>Returns an instance of T object from a <see cref="System.IO.TextReader"/> object</summary>
        /// <param name="reader"><see cref="System.IO.TextReader"/> instance.</param>
        /// <returns>T object</returns>
        public T Deserialize(TextReader reader)
        {
            var o = (T) _xmlSerializer.Deserialize(reader);
            o.Init();
            reader.Close();
            return o;
        }

        /// <summary>Serializes T object to an <see cref="System.Xml.XmlDocument"/> object.</summary>
        /// <param name="xObj">T object to serialize.</param>
        /// <returns>An <see cref="System.Xml.XmlDocument"/> instance.</returns>
        public XmlDocument Serialize(T xObj)
        {
            string xml = StringSerialize(xObj);
            var doc = new XmlDocument {PreserveWhitespace = true};
            doc.LoadXml(xml);
            doc = Clean(doc);
            return doc;
        }

        /// <summary>
        /// Serializes T object to a xml string
        /// </summary>
        /// <param name="xObj">T object</param>
        /// <returns>Xml string</returns>
        public string StringSerialize(T xObj)
        {
            TextWriter w = WriterSerialize(xObj);
            string xml = w.ToString();
            w.Close();
            return xml.Trim();
        }

        /// <summary>
        /// Serializes the T object and returns a <see cref="System.IO.TextWriter"/> object
        /// </summary>
        /// <param name="xObj">T object</param>
        /// <returns><see cref="System.IO.TextWriter"/> object</returns>
        public TextWriter WriterSerialize(T xObj)
        {
            TextWriter w = new StringWriter();
            xObj.Close();
            _xmlSerializer = !String.IsNullOrEmpty(xObj.DefaultNameSpace)
                                 ? new XmlSerializer(_type, xObj.DefaultNameSpace)
                                 : new XmlSerializer(_type);
            _xmlSerializer.Serialize(w, xObj);
            w.Flush();
            return w;
        }

        private static XmlDocument Clean(XmlDocument doc)
        {
            doc.RemoveChild(doc.FirstChild);
            XmlNode first = doc.FirstChild;
            foreach (XmlNode n in doc.ChildNodes)
            {
                if (n.NodeType == XmlNodeType.Element)
                {
                    first = n;
                    break;
                }
            }
            if (first.Attributes != null)
            {
                XmlAttribute a = first.Attributes["xmlns:xsd"];
                if (a != null)
                {
                    first.Attributes.Remove(a);
                }
                a = first.Attributes["xmlns:xsi"];
                if (a != null)
                {
                    first.Attributes.Remove(a);
                }
            }
            return doc;
        }

        /// <summary>Reads object data from a xml file without Decryption.</summary>
        /// <param name="file">Xml file name.</param>
        /// <returns>T object</returns>
        public static T ReadFile(string file)
        {
            return ReadFile(file, false);
        }

        /// <summary>Reads object data from a xml file.</summary>
        /// <param name="file">Xml file name.</param>
        /// <returns>T object</returns>
        /// <param name="decrypt">Decrypt during reading or not</param>
        public static T ReadFile(string file, bool decrypt)
        {
            T xObj;
            var serializer = new XObjectSerializer<T>();
            try
            {
                string xml;
                using (var reader = new StreamReader(file))
                {
                    xml = reader.ReadToEnd();
                    reader.Close();
                }

                if (decrypt)
                    xml = Security.Decrypt(xml);
                xObj = serializer.Deserialize(xml);
                return xObj;
            }
            catch (Exception ex)
            {
                Trace.WriteLine(ex);
            }
            xObj = new T();
            xObj.Init();
            return xObj;
        }

        /// <summary>Writes object data to xml file without Encryption.</summary>
        /// <param name="file">Xml file name.</param>
        /// <param name="xObj">T object.</param>
        public static bool WriteFile(string file, T xObj)
        {
            return WriteFile(file, xObj, false);
        }

        /// <summary>Writes object data to xml file.</summary>
        /// <param name="file">Xml file name.</param>
        /// <param name="xObj">T object.</param>
        /// <param name="encrypt">Encrypt during saving or not</param>
        public static bool WriteFile(string file, T xObj, bool encrypt)
        {
            bool ok = false;
            var serializer = new XObjectSerializer<T>();
            try
            {
                string xml = serializer.Serialize(xObj).OuterXml;
                if (encrypt)
                    xml = Security.Encrypt(xml);
                using (var writer = new StreamWriter(file, false))
                {
                    writer.Write(xml.Trim());
                    writer.Flush();
                    writer.Close();
                }
                ok = true;
            }
            catch (Exception ex)
            {
                Trace.WriteLine(ex);
            }
            return ok;
        }
    }
}